package cn.topevery.garden.wapi.utils;


import cn.topevery.garden.wapi.model.SsoResult;
import cn.topevery.garden.wapi.model.TokenParam;
import cn.topevery.garden.wapi.model.TokenResult;
import hw.topevery.framework.Action;
import hw.topevery.framework.Utility;
import hw.topevery.sso.framework.jwt.JwtTokenUtil;
import org.apache.http.*;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.apache.shiro.codec.Base64;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

@Component
public class TokenHelper {
    private static String clientId;
    private static String tokenUrl;
    private static String refreshTokenUrl;
    private static String logoutUrl;
    private static String userInfoUrl;
    private static String clientSecret;

    @Value("${hw.sso.clientId}")
    private void setClientId(String clientId) {
        TokenHelper.clientId = clientId;
    }

    @Value("${hw.sso.clientSecret}")
    private void setClientSecret(String clientSecret) {
        TokenHelper.clientSecret = clientSecret;
    }

    @Value("${hw.sso.loginUrl}")
    private void setTokenUrl(String tokenUrl) {
        TokenHelper.tokenUrl = tokenUrl;
    }

    @Value("${hw.sso.userInfoUrl}")
    private void setUserInfoUrl(String userInfoUrl) {
        TokenHelper.userInfoUrl = userInfoUrl;
    }

    public static TokenResult getTokenResult(TokenParam param) throws Exception {
        AtomicReference<TokenResult> result = new AtomicReference<>(new TokenResult());
        Action<HttpRequest> requestAction = httpRequest -> {
            httpRequest.addHeader(new BasicHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"));
            List<NameValuePair> params = new ArrayList<>();
            params.add(new BasicNameValuePair("client_id", clientId));
            params.add(new BasicNameValuePair("grant_type", "password"));
            params.add(new BasicNameValuePair("password", param.getPassword()));
            params.add(new BasicNameValuePair("username", param.getUsername()));
            params.add(new BasicNameValuePair("client_secret", param.getClient_secret()));

            ((HttpPost) httpRequest).setEntity(new UrlEncodedFormEntity(params, StandardCharsets.UTF_8));
        };
        return getTokenResult(result, requestAction, tokenUrl);
    }

    public static TokenResult refreshToken(String refreshToken) throws Exception {
        AtomicReference<TokenResult> result = new AtomicReference<>(new TokenResult());
        Action<HttpRequest> requestAction = httpRequest -> {
            httpRequest.addHeader(new BasicHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"));
            List<NameValuePair> params = new ArrayList<>();
            params.add(new BasicNameValuePair("client_id", clientId));
            params.add(new BasicNameValuePair("grant_type", "refresh_token"));
            params.add(new BasicNameValuePair("refresh_token", refreshToken));

            ((HttpPost) httpRequest).setEntity(new UrlEncodedFormEntity(params, StandardCharsets.UTF_8));
        };
        return getTokenResult(result, requestAction, refreshTokenUrl);
    }

    private static TokenResult getTokenResult(AtomicReference<TokenResult> result, Action<HttpRequest> requestAction, String refreshTokenUrl) throws Exception {
        Action<HttpResponse> responseAction = httpResponse -> {
            HttpEntity httpEntity = httpResponse.getEntity();
            String tokenJosn = httpEntity.toString();
            result.set(Utility.jsonDeserialize(tokenJosn, TokenResult.class));
        };

        Utility.httpPostReqData(refreshTokenUrl, 30000, null, requestAction, responseAction);

        return result.get();
    }

    public static TokenResult getTokenByCode(String code) throws Exception {
        AtomicReference<TokenResult> result = new AtomicReference<>(new TokenResult());

        List<NameValuePair> params = new ArrayList<NameValuePair>();
        params.add(new BasicNameValuePair("code", code));
        params.add(new BasicNameValuePair("grant_type", "authorization_code"));
        Action<HttpRequest> requestAction = httpRequest -> {
            httpRequest.addHeader(new BasicHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"));
            httpRequest.addHeader(new BasicHeader("Authorization", getAuthorizationHeader(clientId, clientSecret)));
            ((HttpPost) httpRequest).setEntity(new UrlEncodedFormEntity(params, StandardCharsets.UTF_8));
        };

        Action<HttpResponse> responseAction = httpResponse -> {
            HttpEntity httpEntity = httpResponse.getEntity();
            String tokenJosn = null;
            try {
                tokenJosn = EntityUtils.toString(httpEntity);
            } catch (IOException e) {
                e.printStackTrace();
            }
            result.set(Utility.jsonDeserialize(tokenJosn, TokenResult.class));
        };

        Utility.httpPostReqData(tokenUrl, 30000, null, requestAction, responseAction);

        return result.get();
    }

    private static String getAuthorizationHeader(String clientId, String clientSecret) {
        //log.warn("Null Client ID or Client Secret detected. Endpoint that requires authentication will reject request with 401 error.");
        String creds = String.format("%s:%s", clientId, clientSecret);

        return "Basic " + new String(Base64.encode(creds.getBytes(StandardCharsets.UTF_8)));
    }

    public static String logout(String token, String userId) throws Exception {
        AtomicReference<String> result = new AtomicReference<>();
        Action<HttpMessage> meassagAction = httpMessage -> {
            httpMessage.addHeader(new BasicHeader(JwtTokenUtil.getTokenName(), token));
        };
        Action<HttpResponse> responseAction = httpResponse -> {
            HttpEntity httpEntity = httpResponse.getEntity();
            String tokenJosn = null;
            try {
                tokenJosn = EntityUtils.toString(httpEntity);
            } catch (IOException e) {
                e.printStackTrace();
            }
            result.set(tokenJosn);
        };

        Utility.httpGetReqData(logoutUrl + userId, 30000, meassagAction, responseAction);

        return result.get();
    }

    public static SsoResult getUserInfo(String token) throws Exception {
        AtomicReference<SsoResult> result = new AtomicReference<>();
        /*Action<HttpMessage> meassagAction = httpMessage -> {
            httpMessage.addHeader(new BasicHeader(JwtTokenUtil.getTokenName(), "bearer " + token));
        };*/
        String url = userInfoUrl + "?token=" + token;
        Action<HttpResponse> responseAction = httpResponse -> {
            HttpEntity httpEntity = httpResponse.getEntity();
            String tokenJosn = null;
            try {
                tokenJosn = EntityUtils.toString(httpEntity);
            } catch (IOException e) {
                e.printStackTrace();
            }
            result.set(Utility.jsonDeserialize(tokenJosn, SsoResult.class));
        };

        Utility.httpGetReqData(url, 30000, null, responseAction);

        return result.get();
    }
}
